
#include <algorithm>

#include "stdafx.h"
#include "UmlExt.h"
#include "CodeLines.h"
#include "PositionInfo.h"
#include "Grammar.h"

GOAL::Code_Lines::Code_Lines (const Udm::Object& parent)
: parent_ (parent)
{
}

GOAL::Code_Lines::~Code_Lines ()
{
}


void GOAL::Code_Lines::insert (const GOAL::MgaObject& object)
{
  // are we looking at an object in a sub component?
  if (parent_ != object.parent ())
    return;

  // does the object already exist? If so, has it only been added
  // and not checked?
  if (!exists(object) || 
      object_lookups_[object]["status"] == GOAL::Status::ADDED)
  {
    // obtain the object's position
    //std::string position = object.position ().operator std::string ();

    // go ahead and grab the Object part of this MgaObject
    Udm::Object downcasted = object;

    // get all adjacent objects
    std::set<Udm::Object> adjacents = downcasted.GetAdjacentUniqueObjects ();
    std::set<Udm::Object>::iterator i = adjacents.begin ();

    unsigned int currentIndex;

    // if status of this object is added, but not checked, then
    // we need to look at it's adjacents
    if (object_lookups_[object]["status"] == GOAL::Status::ADDED)
      {
        currentIndex = find (object);
      }
    else
      {
        // this is a brand new object. insert it into a new code line
        currentIndex = code_lines_.size ();

        // resize the code lines if necessary in 10 line chunks (saves
        // from constant allocation
        if (currentIndex % 10 == 0)
          //code_lines_.resize (currentIndex + 10);
          code_lines_.reserve (currentIndex + 10);

        code_lines_.push_back (Code_Line ());
      }
     
    // set the code line
    Code_Line & new_code_line= code_lines_[currentIndex];

    // insert the object into the code line
    new_code_line.insert (object);
    
    // change the status of this object to checked and update
    // the index for this object
    object_lookups_[object]["index"] = currentIndex;
    object_lookups_[object]["status"] = GOAL::Status::CHECKED;

    int count = 1;
    for (;i != adjacents.end (); ++i, ++count)
    {
      GOAL::MgaObject iUpCast = GOAL::MgaObject::Cast (*i);

      if (iUpCast.GetParent () != parent_ )
        continue;

      if (!exists (iUpCast))
        {
          // we've never seen the adjacent object. Update the status
          // to added so we know to fully check it later.
          new_code_line.insert (iUpCast);
          
          this->object_lookups_[iUpCast]["index"] = currentIndex;
          this->object_lookups_[iUpCast]["status"] = GOAL::Status::ADDED;
        }
      else
        {
          // the object existed. we need to see if its index is lower
          // than ours, which would mean we need to merge the lines
          unsigned int adjacentArrayIndex = find (iUpCast);

          // if we are connected to an element in a code line
          // earlier in the list, merge our list with theirs
          if (adjacentArrayIndex < currentIndex)
            {
               merge (new_code_line,code_lines_[adjacentArrayIndex]);
               reroute (currentIndex,adjacentArrayIndex);
               code_lines_[currentIndex].objects_.clear ();
               new_code_line = code_lines_[currentIndex];
               currentIndex = adjacentArrayIndex;
            }
          else if (adjacentArrayIndex > currentIndex)
            {
              // only other option is that the index of this object
              // is later than our current. This happens when we merged
              // with an earlier line earlier in the loop
              merge (code_lines_[adjacentArrayIndex], new_code_line);
              reroute (adjacentArrayIndex,currentIndex);
              code_lines_[adjacentArrayIndex].objects_.clear ();
            }
        }
    }
  }
}


GOAL::Code_Line & 
GOAL::Code_Lines::operator[] (const MgaObject &object)
{
  return code_lines_[find (object)];
}

GOAL::Code_Line & GOAL::Code_Lines::operator[] (unsigned int index)
{
  return this->code_lines_[index];
}

const GOAL::Code_Line & GOAL::Code_Lines::operator[] (unsigned int index) const
{
  return this->code_lines_[index];
}

unsigned int GOAL::Code_Lines::size ()
{
  return this->code_lines_.size ();
}


void GOAL::Code_Lines::reroute (unsigned int source, unsigned int dest)
{
  // go through all of the lookups and check for the source. If the 
  // current lookup is pointing to source, change it to point to dest.

  std::map<MgaObject,std::map<std::string, 
          GOAL::Status::type> >::iterator i = object_lookups_.begin ();

  for (; i != object_lookups_.end (); ++i)
    {
      if (i->second["index"] == source)
        {
          i->second["index"] = dest;
        }
    }
}

void GOAL::Code_Lines::merge (GOAL::Code_Line& source, GOAL::Code_Line& dest)
{
  // bypass the Code_Line layer. We're friends, so we touch each other's
  // private areas :P

  if (source.position.first < dest.position.first)
    {
      dest.position.first = source.position.first;
    }

  if (source.position.second < dest.position.second)
    {
      dest.position.second = source.position.second;
    }

//  std::string indexString 

  unsigned int j = dest.objects_.size ();
  for (unsigned int i = 0; i < source.size (); ++i)
    {
      if (!dest.exists (source[i]))
        {
          if (j % 10 == 0)
            dest.objects_.reserve (j + 10);

          dest.objects_.push_back (source[i]);

//          GOAL::MgaObject thisObject = source.objects_[i];

//          dest.object_lookups_[thisObject]["index"] = j;
//          dest.object_lookups_[thisObject]["status"] = source.object_lookups_[thisObject]["status"];
          ++j;
        }
    }

  // insert the source list into the dest list

//  dest.objects_.insert (dest.objects_.end (),
//                      source.objects_.begin (), source.objects_.end ());
}

void GOAL::Code_Lines::prune (void)
{
  std::vector<Code_Line>::iterator next = code_lines_.begin ();
  std::vector<Code_Line>::iterator empty = code_lines_.begin ();

  std::vector<Code_Line>::iterator lastValid = code_lines_.begin ();

  if (lastValid == code_lines_.end ())
    return;

  unsigned int i = 0, j = 0;

  while (next != code_lines_.end ())
    {
      // find the next open slot in the vector
      for (; empty != code_lines_.end () && empty->size () > 0; ++empty, ++i)
        lastValid = empty;

      // if empty passed the next pointer
      if (i > j)
        {
          // if empty is not already at the end, set next to empty + 1
          if (empty != code_lines_.end ())
            {
              j = i + 1;
              next = empty + 1;
            }
          else
            break;
        }

      // if we broke out of the loop, then we won't be at the
      // end of the vector. The situation should be such that 
      for (; next != code_lines_.end () && next->size () == 0; ++next, ++j);

      if (next != code_lines_.end ())
        {
          merge (code_lines_[j],code_lines_[i]);
          reroute (j,i);
        }
    }

  code_lines_.erase (lastValid + 1,code_lines_.end ());
  
}

GOAL::Code_Lines GOAL::Code_Lines::getSorted (void)
{
  Code_Lines newCopy (*this);
  std::sort (newCopy.code_lines_.begin (),newCopy.code_lines_.end ());

  return newCopy;
  /// get a sorted copy of this Code Lines (by y position)
}

bool 
GOAL::Code_Lines::exists (const GOAL::MgaObject& object)
{  
  return this->object_lookups_.find (object) != this->object_lookups_.end ();
}

unsigned int
GOAL::Code_Lines::find (const GOAL::MgaObject& object)
{  
  std::map<MgaObject, std::map<std::string, 
    GOAL::Status::type> >::iterator i = this->object_lookups_.find (object);

  if (i != this->object_lookups_.end ())
    return i->second["index"];

  else return 0-1;
}
